﻿using System.Buffers;
using System.Net;
using System.Net.Sockets;

namespace GCC.Container.Abstracts
{
    /// <summary>
    /// 附带具体类型的TCP通信客户端
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class AbsCommUDPClient<T> : AbsCommClient, IDisposable
    {
        /// <summary>
        /// 是否已经销毁
        /// </summary>
        protected bool _disposed;

        /// <summary>
        /// 日志对象
        /// </summary>
        protected readonly AbsGCCLogger _logger;

        /// <summary>
        /// 头长度
        /// </summary>
        protected int _headSize = 0;

        /// <summary>
        /// 当获取到数据时事件
        /// </summary>
        public event Action<T>? GotData;

        /// <summary>
        /// 当获取到数据时事件
        /// </summary>
        internal event Action<T, IPEndPoint>? GotInternalData;

        /// <summary>
        /// 尝试发送数据时
        /// </summary>
        internal event Func<Memory<byte>, IPEndPoint, bool>? TrySend;

        /// <summary>
        /// 当销毁时调用
        /// </summary>
        public event Action<IPEndPoint?>? OnDisposing;

        /// <summary>
        /// 目标IP
        /// </summary>
        protected IPEndPoint? _target;

        /// <summary>
        /// 本地绑定的地址
        /// </summary>
        protected IPEndPoint? _localEndPoint;

        /// <summary>
        /// 套接字对象
        /// </summary>
        protected Socket? _socket;

        /// <summary>
        /// 当前的发送目标
        /// </summary>
        public IPEndPoint? TargetEndPoint { get => _target; }
        /// <summary>
        /// 当前的发送目标
        /// </summary>
        public IPEndPoint? LocalEndPoint
        {
            get
            {
                if (_localEndPoint is not null)
                {
                    return _localEndPoint;
                }
                else if (_socket is not null && _socket.LocalEndPoint is not null)
                {
                    return (IPEndPoint)_socket.LocalEndPoint;
                }
                return null;
            }
        }

        /// <summary>
        /// 取消token
        /// </summary>
        protected readonly CancellationTokenSource _cancellToken = new();
        /// <summary>
        /// 当前服务的缓冲池
        /// </summary>
        protected readonly MemoryPool<byte> _memoryPool = MemoryPool<byte>.Shared;

        /// <summary>
        /// 析构函数
        /// </summary>
        ~AbsCommUDPClient()
        {
            Dispose(false);
        }

        /// <summary>
        /// 销毁模型
        /// </summary>
        /// <param name="flag">是否是用户主动调用</param>
        protected virtual void Dispose(bool flag)
        {
            if (_disposed)
            {
                return;
            }
            _disposed = true;


            try
            {
                _cancellToken.Cancel();
                _memoryPool.Dispose();
                _cancellToken.Dispose();
                OnDisposing?.Invoke(this._target);
                _socket?.Dispose();
            }
            catch (Exception ex)
            {
                _logger.Error("AbsCommTcpClient dispose error.", exception: ex);
            }
        }

        /// <summary>
        /// 销毁模型
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }



        /// <summary>
        /// 构造函数
        /// </summary> 
        /// <param name="localInfo">本地地址信息</param>
        /// <param name="target">目标地址信息</param>
        /// <param name="logger">日志</param>
        /// <param name="name">名称</param>
        public AbsCommUDPClient(IPEndPoint? localInfo, AbsGCCLogger logger, string name, IPEndPoint? target = null)
        {
            ServerType = "UDP";
            _logger = logger;
            Name = name;
            _target = target;
            _localEndPoint = localInfo;
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        }

        /// <summary>
        /// 启动监听
        /// </summary>
        public void StartReceive()
        {
            if (_socket is null || _localEndPoint is null)
            {
                return;
            }
            if (_localEndPoint is not null && _socket.LocalEndPoint is null)
            {
                _socket.Bind(_localEndPoint);
            }
            _ = Task.Factory.StartNew(() =>
            {
                using var bufferOwner = _memoryPool.Rent(64 * 1024);
                var buffer = bufferOwner.Memory;
                EndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
                while (!_cancellToken.Token.IsCancellationRequested)
                {
                    try
                    {
                        var dataCount = _socket.ReceiveFrom(buffer.Span, ref endPoint);
                        _ = Task.Run(() =>
                          {
                              if (_headSize != 0 && CheckHeader(buffer[.._headSize]))
                              {
                                  return;
                              }
                              var dataInstance = HandleData(buffer[.._headSize], buffer[_headSize..dataCount]);
                              GotInternalData?.Invoke(dataInstance, (IPEndPoint)endPoint);
                              GotData?.Invoke(dataInstance);
                          });

                    }
                    catch (Exception ex)
                    {
                        _logger.Error($"UDP Client got error.", exception: ex);
                        continue;
                    }
                }
            }, _cancellToken.Token,
            TaskCreationOptions.LongRunning,
            TaskScheduler.Default);
        }

        /// <summary>
        /// 触发一次数据发送
        /// </summary>
        /// <param name="data">数据</param>
        internal void TriggerDataEvent(T data)
        {
            GotData?.Invoke(data);
        }

        /// <summary>
        /// 发送数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool Send(T data)
        {
            try
            {
                int totalLen = _headSize + GetBodyLengh(data);
                using var allDataBufferOwner = _memoryPool.Rent(totalLen);
                var allDataBuffer = allDataBufferOwner.Memory[..totalLen];

                PutHeader(allDataBuffer[.._headSize], data);
                PutData(allDataBuffer[.._headSize], allDataBuffer[_headSize..], data);

                if (TrySend is null && _target is not null && _socket is not null)
                {
                    var res = _socket.SendTo(allDataBuffer.Span, _target) == allDataBuffer.Length;

                    if (_localEndPoint is null && res && _socket.LocalEndPoint is not null)
                    {
                        _localEndPoint = (IPEndPoint)_socket.LocalEndPoint;
                        StartReceive();
                    }
                    return res;
                }
                else if (TrySend is not null && _target is not null)
                {
                    return TrySend?.Invoke(allDataBuffer, _target) ?? false;
                }
                return false;

            }
            catch (Exception ex)
            {
                _logger.Error($"Client {Name} send data error", exception: ex);
                return false;
            }
        }

        /// <summary>
        /// 发送一个裸数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="target"></param>
        /// <returns></returns>
        internal bool SendRawData(Memory<byte> data, IPEndPoint target)
        {
            if (_socket is null)
            {
                return false;
            }
            return _socket.SendTo(data.Span, target) == data.Length;
        }

        /// <summary>
        /// 检查头
        /// </summary>
        /// <param name="headBuffer">头部缓冲</param>
        /// <returns></returns>
        protected abstract bool CheckHeader(in Memory<byte> headBuffer);

        /// <summary>
        /// 处理数据
        /// </summary>
        /// <param name="headBuffer">头部buffer</param>
        /// <param name="bodyBuffer">数据buffer</param>
        protected abstract T HandleData(in Memory<byte> headBuffer, in Memory<byte> bodyBuffer);


        /// <summary>
        /// 获取数据的内容长度
        /// </summary>
        /// <param name="data"></param>
        protected abstract int GetBodyLengh(T data);


        /// <summary>
        /// 检查头
        /// </summary>
        /// <param name="headBuffer">头部缓冲</param> 
        /// <param name="data">数据</param>
        /// <returns></returns>
        protected abstract void PutHeader(Memory<byte> headBuffer, in T data);

        /// <summary>
        /// 处理数据
        /// </summary>
        /// <param name="headBuffer">头部buffer</param>
        /// <param name="bodyBuffer">数据buffer</param>
        /// <param name="data">数据</param>
        protected abstract void PutData(in Memory<byte> headBuffer, Memory<byte> bodyBuffer, in T data);


    }
}
